package org.jeecg.modules.fund.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.fund.entity.*;
import org.jeecg.modules.fund.mapper.FundTradeMapper;
import org.jeecg.modules.fund.mapper.FundValueMapper;
import org.jeecg.modules.fund.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class FundTradeServiceImpl extends ServiceImpl<FundTradeMapper, FundTrade> implements IFundTradeService {
    @Autowired
    private IFundValueService fundValueService;
    @Autowired
    private FundValueMapper fundValueMapper;
    @Autowired
    private IFundLibraryService libraryService;
    @Autowired
    private IFundHoldService fundHoldService;
    @Autowired
    private IFundCalendarCnService calendarCnService;
    @Autowired
    private IFundIncomeService fundIncomeService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<FundTrade> createTrade(FundTrade trade) {
        // 设置默认状态
        trade.setTradeStatus(0);
        // 判断买入日期是今天还是之前的日期,<0说明交易日期小于今日日期、>0说明交易日期大于今日日期、等于0说明日期相同
        int betweenDay = DateUtil.compare(trade.getTradeDate(), new Date(), "yyyy-MM-dd");
        // 真实交易日期
        String tradeDate = createRealTradeDate(trade.getTradeDate(), trade.getTradeDateType(), "");
        // 获取基金详情
        FundLibrary fundLibrary = libraryService.getOne(new LambdaQueryWrapper<FundLibrary>().eq(FundLibrary::getCode, trade.getCode()));
        // 获取基金的持有情况
        FundHold fundHold = fundHoldService.getById(trade.getHoldId());
        // 净值表名
        String tableName = fundValueService.tableName(trade.getCode());
        // 查询一下该日期底下是否存在净值
        FundValue value = fundValueMapper.selectByDateAndFundId(tableName, tradeDate, fundLibrary.getId());
        if (betweenDay < 0) {
            if (value != null && value.getIsEstimate() == 0) {
                // 此时可直接说明已经是更新过的数据，可以直接将状态修改成完成状态
                // 判断一下卖出的份额还够不够
                if (trade.getTradeType() == 1 && fundHold.getHoldNum().compareTo(trade.getTradeNum()) < 0) {
                    return Result.error("减仓份额不足");
                }
                // 买入情况可直接设置成功
                trade.setTradeStatus(1);
                // 插入真正的交易日期
                trade.setTradeSuccessDate(DateUtil.parseDate(tradeDate));
            }
        }
        if (trade.getTradeType() == 0) {
            /* 买入操作 */
            if (trade.getTradeStatus() == 1 && value != null) {
                // 需计算最终的买入份额 = 买入金额 ÷（1+费率）÷ 净值
                BigDecimal rate = BigDecimal.ONE
                        .add(trade.getTradeRate().multiply(new BigDecimal("0.01")));
                // 最终份额
                BigDecimal num = trade.getTradeMoney()
                        .divide(rate, 2, BigDecimal.ROUND_HALF_UP)
                        .divide(value.getNowVal(), 2, BigDecimal.ROUND_HALF_UP);
                // 设置份额
                trade.setTradeNum(num);
                // 计算手续费
                trade.setTradeCommission(trade.getTradeMoney().multiply(trade.getTradeRate()));

                /* 主表数据处理 */
                // 重新添加份额
                fundHold.setHoldNum(fundHold.getHoldNum().add(trade.getTradeNum()));
                // 重新添加成本
                fundHold.setHoldCost(fundHold.getHoldCost().add(trade.getTradeMoney()));
                // 更新主表
                fundHoldService.updateById(fundHold);
            }
            // 保存子表数据
            this.save(trade);
            // 批量更新收益数据
            createInComeBatch(trade.getHoldId(), fundHold.getFundId(), tableName);
        } else if (trade.getTradeType() == 1) {
            /* 卖出操作 */
            if (trade.getTradeStatus() == 1 && value != null) {
                // 赎回费用 = 份额 * 净值 * 费率
                trade.setTradeCommission(trade.getTradeNum().multiply(value.getNowVal()).multiply(trade.getTradeRate()));
                // 设置实际交易金额:卖出金额 = 赎回份额 × 赎回当日基金份额净值 - 赎回费用
                trade.setTradeMoney(trade.getTradeNum().multiply(value.getNowVal()).subtract(trade.getTradeCommission()));

                // 主表操作,①减份额 ②减卖出额
                fundHold.setHoldNum(fundHold.getHoldNum().subtract(trade.getTradeNum()));
                fundHold.setSaleMoney(fundHold.getSaleMoney().add(trade.getTradeMoney()));
                // 优化主表
                fundHoldService.updateById(fundHold);
            }
            // 更新子表
            this.save(trade);
        } else {
            return Result.error("创建交易信息失败");
        }
        return Result.ok(trade);
    }

    @Override
    public String createRealTradeDate(Date tradeDate, Integer tradeDateType, String fundId) {
        // TODO：判断是否是QDII
        // 判断是否是周末
        Integer dateType = DateUtil.isWeekend(tradeDate) ? 1 : tradeDateType;
        // 查询日期
        Date date = DateUtil.offsetDay(tradeDate, tradeDateType);
        // 查询是否休市
        FundCalendarCn calendarCn = calendarCnService.getOne(new LambdaQueryWrapper<FundCalendarCn>().eq(FundCalendarCn::getDateName, DateUtil.format(date, "yyyy-MM-dd")).eq(FundCalendarCn::getIsOpen, 0));
        // 没有开市，则继续获取数据
        if (calendarCn == null) {
            return createRealTradeDate(date, dateType, fundId);
        }
        return DateUtil.format(calendarCn.getDateName(), "yyyy-MM-dd");
    }

    /**
     * 批量更新收益
     *
     * @param holdId
     * @param fundId
     * @param tableName
     */
    public void createInComeBatch(String holdId, String fundId, String tableName) {
        // 列出所有有效的交易记录
        List<FundTrade> tradeList = this.list(new LambdaQueryWrapper<FundTrade>().eq(FundTrade::getHoldId, holdId).eq(FundTrade::getTradeStatus, 1).orderByAsc(FundTrade::getTradeSuccessDate));
        // 当前份额
        BigDecimal tempNum = BigDecimal.ZERO;
        // 遍历处理数据
        for (int i = 0; i < tradeList.size(); i++) {
            // 实体
            FundTrade trade = tradeList.get(i);
            // 上个数据
            FundTrade lastTrade = i - 1 < 0 ? null : tradeList.get(i - 1);
            // 设置参数份额
            if (trade.getTradeType() == 1) {
                // 卖出则份额减掉
                tempNum = tempNum.subtract(trade.getTradeNum());
            } else if (trade.getTradeType() == 0) {
                // 买入则份额新增
                tempNum = tempNum.add(trade.getTradeNum());
            } else {
                // 修改
                tempNum = trade.getTradeNum();
            }
            // 设置上次的总计值
            trade.setLastNum(lastTrade == null ? BigDecimal.ZERO : lastTrade.getTotalNum());
            // 设置累计值
            trade.setTotalNum(tempNum);
        }
        if (!tradeList.isEmpty()) {
            // 数据列表
            List<FundIncome> fundIncomeList = new ArrayList<>();
            // 获取开盘日期
            List<FundCalendarCn> calendarCns = calendarCnService.list(new LambdaQueryWrapper<FundCalendarCn>()
                    .ge(FundCalendarCn::getDateName, tradeList.get(0).getTradeSuccessDate())
                    .le(FundCalendarCn::getDateName, new Date()));
            int temp = 0;
            for (int i = 0; i < tradeList.size(); i++) {
                FundTrade trade = tradeList.get(i);
                // 判断一下是否有下一个操作
                FundTrade nextTrade = i + 1 < tradeList.size() ? tradeList.get(i + 1) : null;
                for (int t = temp; t < calendarCns.size(); t++) {
                    FundCalendarCn calendarCn = calendarCns.get(t);
                    if (nextTrade == null) {
                        // 最后一个操作
                        FundIncome income = new FundIncome()
                                .setHoldId(holdId)
                                .setIncDate(calendarCn.getDateName())
                                .setIncTotalNum(trade.getTotalNum());
                        if (calendarCn.getIsOpen() == 0) {
                            FundValue value = fundValueMapper.selectByDateAndFundId(tableName, DateUtil.format(calendarCn.getDateName(), "yyyy-MM-dd"), fundId);
                            if (value != null) {
                                // 判断一下如果第一个是买入，则不计算在内
                                boolean isBuy = DateUtil.compare(trade.getTradeSuccessDate(), calendarCn.getDateName(), "yyyy-MM-dd") == 0 ? true : false;
                                // 倍率
                                BigDecimal bx = BigDecimal.ONE.add(value.getValRate().multiply(new BigDecimal("0.01")));
                                // 上次的值
                                BigDecimal lastVal = value.getNowVal().divide(bx, 5, BigDecimal.ROUND_HALF_UP);
                                // 计算收益金额
                                BigDecimal amt = (isBuy ? trade.getLastNum() : trade.getTotalNum()).multiply(value.getNowVal().subtract(lastVal));
                                income.setIncVal(value.getNowVal());
                                income.setIncLastVal(lastVal);
                                income.setIncRate(value.getValRate());
                                income.setIncAmt(amt);
                                income.setIncNum(isBuy ? trade.getLastNum() : trade.getTotalNum());
                                income.setRemark(isBuy ? DateUtil.format(trade.getTradeSuccessDate(),"yyyy-MM-dd") + "存在交易行为" : "");
                            }
                        }
                        fundIncomeList.add(income);
                    } else {
                        // <0说明交易日期小于开盘日期、>0说明交易日期大于开盘日期、等于0说明日期相同
                        int betweenDay = DateUtil.compare(nextTrade.getTradeSuccessDate(), calendarCn.getDateName(), "yyyy-MM-dd");
                        if (betweenDay != 0) {
                            FundIncome income = new FundIncome().setHoldId(holdId).setIncDate(calendarCn.getDateName()).setIncNum(trade.getTotalNum()).setIncTotalNum(trade.getTotalNum());
                            if (calendarCn.getIsOpen() == 0) {
                                FundValue value = fundValueMapper.selectByDateAndFundId(tableName, DateUtil.format(calendarCn.getDateName(), "yyyy-MM-dd"), fundId);
                                // 计算买入日期是否与当前日期一致
                                int nowBuyDayBetween = DateUtil.compare(trade.getTradeSuccessDate(),calendarCn.getDateName(),"yyyy-MM-dd");
                                if (value != null && nowBuyDayBetween != 0) {
                                    // 上次的值
                                    BigDecimal lastVal = value.getNowVal().divide(BigDecimal.ONE.add(value.getValRate().multiply(new BigDecimal("0.01"))), 5, BigDecimal.ROUND_HALF_UP);
                                    // 计算收益金额
                                    BigDecimal amt = trade.getTotalNum().multiply(value.getNowVal().subtract(lastVal));
                                    income.setIncVal(value.getNowVal());
                                    income.setIncLastVal(lastVal);
                                    income.setIncRate(value.getValRate());
                                    income.setIncAmt(amt);
                                } else if(nowBuyDayBetween == 0) {
                                    income.setRemark(DateUtil.format(trade.getTradeSuccessDate(),"yyyy-MM-dd") + "存在交易行为");
                                }
                            }
                            fundIncomeList.add(income);
                        } else {
                            temp = t;
                            break;
                        }
                    }
                }
            }
            // 删除旧数据
            fundIncomeService.remove(new LambdaUpdateWrapper<FundIncome>().eq(FundIncome::getHoldId, holdId));
            // 批量插入
            fundIncomeService.saveBatch(fundIncomeList);
        }
    }
}
